// 引入请求上下文
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
// 引入模型
import Article from '../../Models/Article'
import Follow from '../../Models/Follow'
import Profile from '../../Models/Profile'
import Tag from '../../Models/Tag'
// 引入验证器
import CreateArticleValidator from '../../Validators/CreateArticleValidator'

/**
 * 构建文章控制器
 * 控制器需要导出，所以使用export default
 */
export default class ArticlesController {

  /**
   * 异步的首页方法
   * @param param0 请求的对象，包含视图，请求体，权限，这些都是从上下文中获取的
   * @returns 
   */
  public async index({ view, request, auth }: HttpContextContract) {
    // 获取搜索关键字，标签
    const { filterBy, tag } = request.qs()
    // 获取分页参数
    const page = request.input('page', 1)
    const limit = 10

    // 获取当前登录用户资料
    const currentProfile = await Profile.findBy('user_id', auth.user?.id ?? '')

    // 使用模型查询文章
    const articles = await Article.query()
      // 如果搜索关键字是tag，并且tag不为空，
      .if(filterBy === 'tag' && !!tag, (query) =>
        // 使用子查询，tags的值为tag
        query.whereHas('tags', (tagsQuery) => tagsQuery.where('value', tag))
      )
      // 如果搜索关键字是订阅，应该是查询粉丝，并且有当前用户的资料，就是用户已经登录，这里使用了回调函数
      .if(filterBy === 'following' && currentProfile, (query) =>
        // 使用了子查询，查询用户资料
        query.whereHas('profile', async (profileQuery) => {
          // 获取订阅id = 当前用户id的粉丝
          const following = Follow.query().where('followerId', currentProfile!.id)
          // 通过粉丝id查询对应的身份信息
          profileQuery.whereIn('id', following.select('followingId'))
        })
      )
      // 预加载用户资料
      .preload('profile')
      // 聚合查询爱好数
      .withCount('favorites')
      // 使用创建日期排序，降序排列
      .orderBy('createdAt', 'desc')
      // 分页
      .paginate(page, limit)

    
    const tags = await Tag.all()
  
    // 这里似乎是组装query路由参数
    if (filterBy) articles.queryString({ filterBy })
    if (tag) articles.queryString({ filterBy, tag })

    // 渲染模板，设置参数
    return view.render('articles/index', {
      articles,
      tags,
      selectedTag: tag,
      followFeed: filterBy === 'following',
    })
  }

  /**
   * 创建文章
   * @param param0 http上下文
   * @returns 
   */
  public async new({ view }: HttpContextContract) {
    // 直接渲染模板
    return view.render('articles/new')
  }

  /**
   * 创建文章写入数据库
   * @param param0 形参似乎是包含需要用到的模块，权限，请求体，响应体
   * @returns 
   */
  public async create({ auth, request, response }: HttpContextContract) {
    // 请求参数验证，并分离出所需的参数
    const { tags, ...values } = await request.validate(CreateArticleValidator)
    // 创建文章
    const article = await auth.user?.related('articles').create(values)

    if (tags) {
      // 查询标签，若没有标签则创建新标签
      const relatedTags = await Tag.fetchOrCreateMany(
        'value',
        tags.split(',').map((value) => ({ value: value.trim() }))
      )
      // 使用模型关联创建文章和标签的对应关系
      await article?.related('tags').sync(relatedTags.map(({ id }) => id))
    }
    // 跳转到文章展示页面
    return response.redirect().toRoute('articles.show', article)
  }

  /**
   * 查询文章
   * @param param0 请求体，视图渲染模块
   * @returns 
   */
  public async show({ request, view }: HttpContextContract) {
    // 查询
    const article = await Article.findByOrFail('slug', request.param('slug'))
    // 预加载
    await article.load((loader) => {
      loader
      // 预加载用户信息
        .load('user', (userLoader) =>
          // 预加载用户资料，粉丝数量
          userLoader.preload('profile', (profileLoader) => profileLoader.withCount('followers'))
        )
        // 预加载评论，
        .load('comments', (commentLoader) =>
          // 评论需要预加载发布人信息，按照评论发布时间降序排列
          commentLoader.preload('author').orderBy('createdAt', 'desc')
        )
    })
    // 预加载统计喜欢本文章的数量
    await article.loadCount('favorites')
    // 渲染到前端页面
    return view.render('articles/show', { article })
  }

  /**
   * 编辑文章
   * @param param0 用到模块 权限，请求，视图，session，响应 均来自于上下文
   * @returns 
   */
  public async edit({ auth, request, view, session, response }: HttpContextContract) {
    // 当前登录用户
    const article = await auth.user
      // 关联的文章 
      ?.related('articles')
      .query()
      // 搜索关键字
      .where('slug', request.param('slug'))
      .first()

      // 如果当前用户没有搜索到这一篇文章，那么需要报错
    if (!article) {
      // 提示用户没有权限，这里使用的session来传递消息，这个好像有点扯了，为啥使用session呢
      session.flashMessages.set('error', 'Unauthorized access of editor')
      // 回到文章展示页面
      return response.redirect().toRoute('articles.show', { slug: request.param('slug') })
    }
    // 预加载文章的标签信息
    await article.load('tags')
    // 渲染前端模板
    return view.render('articles/edit', { article })
  }

  /**
   * 更新文章
   * @param param0 
   * @returns 
   */
  public async update({ auth, request, session, response }: HttpContextContract) {
    // 查询当前用户是否有这一篇文章
    const article = await auth.user
      ?.related('articles')
      .query()
      .where('slug', request.param('slug'))
      .first()

    // 如果没有找到符合要求的文章，则说明没有权限 
    if (!article) {
      session.flashMessages.set('error', 'Unauthorized update of article')
    } else {
      // 验证请求参数是否符合要求
      const { tags, ...values } = await request.validate(CreateArticleValidator)
      // 保存数据到数据库
      await article.merge(values).save()
      if (tags) {
        // 查询或者创建标签
        const relatedTags = await Tag.fetchOrCreateMany(
          'value',
          tags.split(',').map((value) => ({ value: value.trim() }))
        )
        // 更新文章的标签
        await article.related('tags').sync(relatedTags.map(({ id }) => id))
      }
    }
    // 返回到文章详情页
    return response.redirect().toRoute('articles.show', { slug: request.param('slug') })
  }

  /**
   * 删除文章
   * @param param0 
   * @returns 
   */
  public async destroy({ auth, request, session, response }: HttpContextContract) {
    // 搜索用户的文章
    const article = await auth.user
      ?.related('articles')
      .query()
      .where('slug', request.param('slug'))
      .first()
    // 没有找到说明没有权限
    if (!article) {
      session.flashMessages.set('error', 'Unauthorized delete of article')
    } else {
      // 删除文章
      await article.delete()
      session.flashMessages.set('success', 'Successfully removed article')
    }

    // 操作完成返回到首页
    return response.redirect().toPath('/')
  }
}
